package com.zlmlive.sql.vo;

import java.util.List;

import org.apache.log4j.Logger;

import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.zlmlive.sql.SqlParser;
import com.zlmlive.sql.SqlVo;
import com.zlmlive.sql.condition.WhereCondition;
import com.zlmlive.sql.exception.SqlParseException;


/**
 * <pre>
 * 支持查询语法：
 * 
 * SELECT
 * {col_name | expr}, ...
 * [FROM table_references
 * [WHERE where_definition]
 * [ORDER BY col_name [ASC | DESC] , ...]
 * [LIMIT {[offset,] row_count}]
 * 
 * WHERE表达式
 * <table border="1">
 *    <thead>
 *  	<tr>
 *  	<th align="left">运算符 </th>
 *  	<th align="left">描述 </th>
 *  	<th align="left">示例 </th></tr>
 *    </thead>
 *    <tbody>
 *  	<tr>  
 *  	<td align="left">= </td>  
 *  	<td align="left">等于 </td>  
 *  	<td align="left">… WHERE age = 18 </td></tr>
 *  	<tr>  
 *  	<td align="left">&lt;&gt; </td>  
 *  	<td align="left">不等于 </td>  
 *  	<td align="left">… WHERE age = 18 </td></tr>
 *  	<tr>  
 *  	<td align="left">&gt; </td>  
 *  	<td align="left">大于 </td>  
 *  	<td align="left">… WHERE age &gt; 18 </td></tr>
 *  	<tr>  
 *  	<td align="left">&lt; </td>  
 *  	<td align="left">小于 </td>  
 *  	<td align="left">… WHERE age &lt; 18 </td></tr>
 *  	<tr>  
 *  	<td align="left">&gt;= </td>  
 *  	<td align="left">大于等于 </td>  
 *  	<td align="left">… WHERE age &gt;= 18 </td></tr>
 *  	<tr>  
 *  	<td align="left">&lt;= </td>  
 *  	<td align="left">小于等于 </td>  
 *  	<td align="left">… WHERE age &lt;= 18 </td></tr>
 *  	<tr>  
 *  	<td align="left">LIKE </td>  
 *  	<td align="left">基于正则表达式的比较 </td>  
 *  	<td align="left">… WHERE name like '^张三$' </td></tr>
 *  	<tr>  
 *  	<td align="left">IN </td>  
 *  	<td align="left">属于 </td>  
 *  	<td align="left">… WHERE type IN (1, 2, 3) </td></tr>
 *  	<tr>  
 *  	<td align="left">NOT IN </td>  
 *  	<td align="left">不属于 </td>  
 *  	<td align="left">… WHERE type NOT IN (1, 2, 3) </td></tr>
 *  	<tr>  
 *  	<td align="left">AND </td>  
 *  	<td align="left">并且 </td>  
 *  	<td align="left">… WHERE name = '张三' AND age = 18 </td></tr>
 *  	<tr>  
 *  	<td align="left">OR </td>  
 *  	<td align="left">或 </td>  
 *  	<td align="left">… WHERE age = 20 OR age = 18 </td></tr>
 *    </tbody>
 *  </table>
 * </pre>
 * 
 * @author Min
 *
 */
public class SelectVo implements SqlVo {

	private static Logger log = Logger.getLogger(SelectVo.class);

	// 列字段
	private List<String> columns;

	private String allColumn;

	private String count;

	private String table;

	private WhereCondition condition;

	private List<OrderByVo> orderByList;

	private LimitVo limit;

	public void setAllColumn(String allColumn) {
		this.allColumn = allColumn;
	}

	public void setColumns(List<String> columns) {
		this.columns = columns;
	}

	public void setCount(String count) {
		this.count = count;
	}

	public void setTable(String table) {
		this.table = table;
	}

	public void setCondition(WhereCondition condition) {
		this.condition = condition;
	}

	public void setOrderByList(List<OrderByVo> orderByList) {
		this.orderByList = orderByList;
	}

	public void setLimit(LimitVo limit) {
		this.limit = limit;
	}

	public void check() {

		if (null == table || 0 == table.length()) {
			throw new SqlParseException("select [table] is null");
		}

		if (null == columns && null == allColumn && null == count) {
			throw new SqlParseException("select [column] is null");
		}

		if (null != condition) {
			condition.check();
		}
	}

	@Override
	public String getTable() {
		return this.table;
	}

	public Object toBSON() {
		return null;
	}

	public String toSQL() {
		StringBuilder builder = new StringBuilder();
		builder.append(SqlParser.SELECT_MARK);
		builder.append(SqlParser.BLANK_MARK);

		if (null == this.count) {
			if (null != allColumn) {
				builder.append(allColumn);
			} else {
				for (int i = 0, n = columns.size(); i < n; i++) {
					if (i > 0) {
						builder.append(",");
						builder.append(SqlParser.BLANK_MARK);
					}
					builder.append(columns.get(i));
				}
			}
		} else {
			builder.append(this.count);
		}

		builder.append(SqlParser.BLANK_MARK);
		builder.append(SqlParser.FROM_MARK);
		builder.append(SqlParser.BLANK_MARK);

		builder.append(this.table);
		if (null != condition) {
			builder.append(SqlParser.BLANK_MARK);
			builder.append(SqlParser.WHERE_MARK);
			builder.append(SqlParser.BLANK_MARK);
			condition.toSQL(builder);
		}

		if (null != this.orderByList) {
			builder.append(SqlParser.BLANK_MARK);
			builder.append(SqlParser.ORDER_BY_MARK);
			builder.append(SqlParser.BLANK_MARK);
			for (int i = 0, n = orderByList.size(); i < n; i++) {
				if (i > 0) {
					builder.append(",");
					builder.append(SqlParser.BLANK_MARK);
				}
				orderByList.get(i).toSQL(builder);
			}
		}

		if (null != limit) {
			limit.toSQL(builder);
		}

		return builder.toString();
	}

	private DBObject getFields() {
		if (null != this.allColumn) {
			return null;
		}
		DBObject fields = new BasicDBObject();
		// fields.put("_id", 0);
		for (int i = 0, n = columns.size(); i < n; i++) {
			// fields.put(columns.get(i), true);
			fields.put(columns.get(i), 1);// 1代表需要指定显示的列
		}
		return fields;
	}

	private DBObject getQuery() {
		if (null != this.condition) {
			DBObject query = new BasicDBObject();
			this.condition.setQuery(query, null);
			return query;
		}
		return null;
	}

	private DBObject getOrderByObject() {
		DBObject orderByObject = null;
		if (null != this.orderByList) {
			orderByObject = new BasicDBObject();
			for (OrderByVo orderByVo : orderByList) {
				for (String column : orderByVo.getColumns()) {
					orderByObject.put(column, orderByVo.getType().getValue());
				}
			}
		}
		return orderByObject;
	}

	private long getQueryCount(DBCollection collection) {
		DBObject query = getQuery();
		if (null == query) {
			return collection.count();
		} else {
			return collection.count(query);
		}
	}

	public DBObject selectOne(DBCollection collection) {
		DBObject fields = getFields();
		DBObject query = getQuery();
		DBObject orderByObject = getOrderByObject();

		log(fields, query, orderByObject);

		if (null == fields && null == orderByObject) {
			return collection.findOne(query);
		} else if (null != fields && null == orderByObject) {
			return collection.findOne(query, fields);
		} else {
			return collection.findOne(query, fields, orderByObject);
		}
	}

	public Object selectVar(DBCollection collection) {
		if (null == this.count) {
			DBObject result = selectOne(collection);
			return result;
		} else {
			return getQueryCount(collection);
		}
	}

	/** 仅获取一个字段 */
	/*
	 * public Object selectVarOneField(XCO one) { if (null == columns ||
	 * columns.size() != 1) { throw new
	 * RuntimeException("SelectOne query, the return column is not unique."); }
	 * String column = columns.get(0); return one.getObjectValue(column); }
	 */

	public DBCursor selectList(DBCollection collection) {
		DBObject fields = getFields();
		DBObject query = getQuery();
		DBObject orderByObject = getOrderByObject();
		DBCursor cursor = null;

		// 日志
		log(fields, query, orderByObject);

		if (null != query && null == fields) {
			cursor = collection.find(query);
		} else if (null == query && null != fields) {
			cursor = collection.find(new BasicDBObject(), fields);
		} else if (null != fields && null != query) {
			cursor = collection.find(query, fields);
		} else {
			cursor = collection.find();
		}

		if (null != orderByObject) {
			cursor.sort(orderByObject);
		}
		if (null != this.limit) {
			if (null == this.limit.getOffset()) {
				cursor.limit(this.limit.getRowCount());
			} else {
				cursor.limit(this.limit.getRowCount());
				cursor.skip(this.limit.getOffset());
			}
		}
		return cursor;
	}

	private void log(DBObject fields, DBObject query, DBObject orderByObject) {
		// db.t_user.find({ "gender" : true , "age" : { "$gte" : 20}},{ "name" : 1 , "age" : 1 , "address" : 1}).sort({ "age" : -1}).limit(2).skip(0)

		if (log.isInfoEnabled()) {
			StringBuffer sql = new StringBuffer();
			sql.append("db.");
			sql.append(table);
			sql.append(".find(");

			if (null != query) {
				sql.append(query.toString());
			}
			if (null != fields) {
				sql.append(",");
				sql.append(fields.toString());
			}
			sql.append(")");
			if (null != orderByObject) {
				sql.append(".sort(");
				sql.append(orderByObject);
				sql.append(")");
			}
			if (null != limit) {
				sql.append(".limit(");
				sql.append(limit.getRowCount());
				sql.append(").skip(");
				sql.append(limit.getOffset());
				sql.append(")");
			}
			log.info(sql.toString());
		}
	}

}
